package cn.qingyun.gis.modules.scenes.job;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.qingyun.gis.exception.CommonException;
import cn.qingyun.gis.modules.express.entity.dto.ExpressRegionNodeDto;
import cn.qingyun.gis.modules.scenes.contant.ComConstant;
import cn.qingyun.gis.modules.scenes.contant.ExcutorType;
import cn.qingyun.gis.modules.scenes.entity.PoiScenario;
import cn.qingyun.gis.modules.scenes.entity.PoiUpdateLog;
import cn.qingyun.gis.modules.scenes.entity.UniAddrRegion;
import cn.qingyun.gis.modules.scenes.mapper.PoiUpdateLogMapper;
import cn.qingyun.gis.modules.scenes.mapper.UniAddrRegionMapper;
import cn.qingyun.gis.modules.scenes.service.IPoiScenarioService;
import cn.qingyun.gis.modules.scenes.service.ISchemaService;
import cn.qingyun.gis.modules.scenes.service.UniAddrRegionService;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.xxl.job.core.handler.annotation.XxlJob;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author LHJ
 * @description
 * @date 2024/10/10 16:58
 */
@Controller
@RequestMapping("/app/scenes/")
@Component
@Slf4j
public class UpdatePoiDataHandler {

    @Resource
    private ISchemaService iSchemaService;

    @Resource
    private IPoiScenarioService iPoiScenarioService;

    @Resource
    private UniAddrRegionMapper uniAddrRegionMapper;

    @Resource
    private PoiUpdateLogMapper poiUpdateLogMapper;

    @Resource
    private ApplicationContext applicationContext;

    //    @Scheduled()
    @GetMapping("/test")
    @DS("scenes_offensive")
    //@XxlJob("updatePoiDataHandler")
    public void execute() {
        //一、备份16张数据表
        log.debug("poi数据更新开始备份数据表======");
        ComConstant.TABLE_List.forEach(tableName -> {
            String bakTableName = String.format("bak.%s_bak_%s", tableName, DatePattern.PURE_DATE_FORMAT.format(DateUtil.date()));
            log.debug("{}表开始备份", bakTableName);
            String sqlBak = String.format("CREATE TABLE IF NOT EXISTS %s AS SELECT * from public.%s", bakTableName, tableName);
            try {
                iSchemaService.executeSqlScript(ExcutorType.PREPAREDSTATEMENT, "scenes_offensive", sqlBak);
            } catch (Exception e) {
                saveLog(bakTableName + "表备份失败" + e.getMessage(), 1, false, null, 0);
                throw new CommonException(bakTableName + "表备份失败" + e.getMessage());
            }
            saveLog(bakTableName + "表备份成功", 1, true, null, 0);
            log.debug("{}表备份结束", bakTableName);
        });
        log.debug("======数据表备份结束======");
        log.debug("======数据表备份开始删除======");
        //删除三天以前的表
        ComConstant.TABLE_List.forEach(tableName -> {
            String bakTableName = String.format("bak.%s_bak_%s", tableName, DatePattern.PURE_DATE_FORMAT.format(DateUtil.offsetDay(DateUtil.date(), -3)));
            log.debug("{}表开始删除", bakTableName);
            String sqlBak = String.format("drop table if exists %s", bakTableName);
            try {
                iSchemaService.executeSqlScript(ExcutorType.PREPAREDSTATEMENT, "scenes_offensive", sqlBak);
            } catch (Exception e) {
                saveLog(bakTableName + "表删除失败" + e.getMessage() + e.getMessage(), 1, false, null, 0);
                throw new CommonException(bakTableName + "表删除失败" + e.getMessage());
            }
            saveLog(sqlBak + "删除sql执行成功", 1, true, null, 0);
            log.debug("{}表删除结束", bakTableName);
        });
        log.debug("======备份表删除结束======");
        //二、匹配数据
        for (String cityCode : ComConstant.CITY_List) {
            UpdatePoiDataHandler updatePoiDataHandler = applicationContext.getBean(UpdatePoiDataHandler.class);
            updatePoiDataHandler.handler(cityCode);
        }
    }


    @Transactional(rollbackFor = Exception.class)
    public void handler(String cityCode){
        log.debug("网格数据更新开始处理的地市码{}", cityCode);
        //1.查询变化的微网格区划
        try {
            List<UniAddrRegion> microGridRegions = uniAddrRegionMapper.selectCodeChanged(5);
            for (UniAddrRegion microGridRegion : microGridRegions) {
                String uarId = microGridRegion.getId();
                String microGridCode = microGridRegion.getCode();
                String microGridName = microGridRegion.getName();
                String gridCode = microGridRegion.getParentCode();
                String gridName = microGridRegion.getParentName();
                //判断微网格编码是否为空
                if (StringUtils.isEmpty(microGridCode)) {
                    String message = "区划主键：" + microGridRegion.getId() + "，微网格码为空值！";
                    saveLog(message, 2, false, microGridRegion.getId(), 0);
                    continue;
                }
                //判断上级网格编码是否为空
                if (StringUtils.isEmpty(gridCode)) {
                    String message = microGridCode + "上级网格编码为空！";
                    saveLog(message, 2, false, microGridCode, 0);
                    continue;
                }
                //判断区码是否为空
                UniAddrRegion addrRegion = uniAddrRegionMapper.selectGridDistrictCityInfo(gridCode);
                if (addrRegion == null || StringUtils.isEmpty(addrRegion.getParentCode())) {
                    String message = microGridCode + "未找到上级区划编码！";
                    saveLog(message, 2, false, microGridCode, 0);
                    continue;
                }
                String regionDisCode = addrRegion.getParentCode();
                String regionDisName = addrRegion.getParentName();
                //判断地市码是否为空
                UniAddrRegion addrCityRegion = uniAddrRegionMapper.selectGridDistrictCityInfo(regionDisCode);
                if (addrCityRegion == null || StringUtils.isEmpty(addrCityRegion.getParentCode())) {
                    String message = microGridCode + "未找到上级地市编码！";
                    saveLog(message, 2, false, microGridCode, 0);
                    continue;
                }
                String regionCityCode = addrCityRegion.getParentCode();
                String regionCityName = addrCityRegion.getParentName();
                //找出原数据中微网格变化的数据
                List<PoiScenario> list = iPoiScenarioService.selectMircoChangedPoiData(uarId, cityCode);
                if (CollectionUtils.isNotEmpty(list)) {
                    log.debug("主键：{}",uarId);
                    list.forEach(poiScenario -> {
                        poiScenario.setMicroGridCode(microGridCode);
                        poiScenario.setMicroGridName(microGridName);
                        poiScenario.setGridCode(gridCode);
                        poiScenario.setGridName(gridName);
                        poiScenario.setRegionDisCode(regionDisCode);
                        poiScenario.setRegionDis(regionDisName);
                        poiScenario.setRegionCityCode(regionCityCode);
                        poiScenario.setRegionCity(regionCityName);
                    });
                    //先删除原数据
                    List<String> idList = list.stream().map(PoiScenario::getId).collect(Collectors.toList());
                    int result = iPoiScenarioService.getBaseMapper().deleteBatchIds(idList);
                    log.debug("数据删除结果{}", result);
                    //插入变化的数据
                    if (result > 0) {
                        saveLog(microGridCode + "微网格变化数据删除", 2, true, microGridCode, idList.size());
                        boolean saveBatch = iPoiScenarioService.saveBatch(list);
                        log.debug("数据插入的结果{}", saveBatch);
                        saveLog(microGridCode + "微网格变化数据插入", 2, saveBatch, microGridCode, idList.size());
                    } else {
                        saveLog(microGridCode + "微网格变化数据删除", 2, false, microGridCode, idList.size());
                    }
                }
            }
        } catch (Exception e) {
            saveLog("数据更新失败," + e.getMessage(), 2, false, null, 0);
            log.debug("更新数据失败,{}", e.getMessage());
            throw new CommonException("数据更新失败！" + e.getMessage());
        }
        //2。查询变化的网格区划数据
        List<UniAddrRegion> uniAddrRegions = uniAddrRegionMapper.selectCodeChanged(4);
        for (UniAddrRegion uniAddrRegion : uniAddrRegions) {
            try {
                String gridCode = uniAddrRegion.getCode();
                String regionDisCode = uniAddrRegion.getParentCode();
                String gridName = uniAddrRegion.getName();
                String regionDis = uniAddrRegion.getParentName();
                //2.2找出网格变化的对应的原始数据，修改其网格/区县/地市码
                List<PoiScenario> list = iPoiScenarioService.selectChangedPoiData(uniAddrRegion.getId(), cityCode);
                if (CollectionUtils.isNotEmpty(list)) {
                    if (StringUtils.isEmpty(gridCode)) {
                        String message = "区划主键：" + uniAddrRegion.getId() + "，网格码为空值！";
                        saveLog(message, 2, false, uniAddrRegion.getId(), 0);
                        continue;
                    }
                    if (StringUtils.isEmpty(regionDisCode)){
                        String message = gridCode + "未找到上级区县编码！";
                        saveLog(message, 2, false, gridCode, 0);
                        continue;
                    }
                    UniAddrRegion addrRegion = uniAddrRegionMapper.selectGridDistrictCityInfo(regionDisCode);
                    if (addrRegion == null || StringUtils.isEmpty(addrRegion.getParentCode())) {
                        String message = gridCode + "未找到上级地市编码！";
                        saveLog(message, 2, false, gridCode, 0);
                        continue;
                    }
                    String regionCityCode = addrRegion.getParentCode();
                    String regionCityName = addrRegion.getParentName();
                    list.forEach(poiScenario -> {
                        poiScenario.setGridCode(gridCode);
                        poiScenario.setGridName(gridName);
                        poiScenario.setRegionDisCode(regionDisCode);
                        poiScenario.setRegionDis(regionDis);
                        poiScenario.setRegionCityCode(regionCityCode);
                        poiScenario.setRegionCity(regionCityName);
                    });
                    //2.3先删除原数据
                    List<String> idList = list.stream().map(PoiScenario::getId).collect(Collectors.toList());
                    int result = iPoiScenarioService.getBaseMapper().deleteBatchIds(idList);
                    log.debug("数据删除结果{}", result);
                    //2.4插入变化的数据
                    if (result > 0) {
                        boolean saveBatch = iPoiScenarioService.saveBatch(list);
                        saveLog(gridCode + "网格变化数据插入", 2, saveBatch, gridCode, idList.size());
                        log.debug("数据插入的结果{}", saveBatch);
                        log.debug("网格变化集合的大小{}", list.size());
                    } else {
                        saveLog(gridCode + "网格变化数据删除", 2, false, gridCode, idList.size());
                    }
                }
            } catch (Exception e) {
                saveLog("网格操作-数据更新失败," + e.getMessage(), 2, false, null, 0);
                log.debug("网格操作-更新数据失败,{}", e.getMessage());
                throw new CommonException("数据更新失败！" + e.getMessage());
            }
        }
    }

    /**
     * 保存操作日志
     *
     * @param message 信息
     * @param type    日志类型
     * @param flag    是否成功标识
     * @param code    操作区划码
     * @param count   操作的个数
     */
    private void saveLog(String message, Integer type, Boolean flag, String code, Integer count) {
        PoiUpdateLog poiUpdateLog = new PoiUpdateLog();
        poiUpdateLog.setCreatTime(new Date());
        poiUpdateLog.setSuccess(flag);
        poiUpdateLog.setSuccessCount(count);
        poiUpdateLog.setCode(code);
        poiUpdateLog.setType(type);
        poiUpdateLog.setMessage(message);
        poiUpdateLogMapper.insert(poiUpdateLog);
    }
}
